﻿#include "svg_helper.h"

// 方便调试时的打印输出
#define qout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]\t"

SvgHelper::SvgHelper() {}

void SvgHelper::parseSvgImage(QString filepath) {
    this->filepath = filepath;

    QFile svgFile(filepath);
    if (svgFile.open(QFile::ReadOnly)) {
        QDomDocument doc;
        if (doc.setContent(&svgFile)) {
            svgFile.close();
            // 返回根节点
            QDomElement root = doc.documentElement();
            qout << "root:" << root.nodeName();
            // 获得第一个子节点
            QDomNode node = root.firstChild();
            while (!node.isNull())  // 如果节点不空
            {
                if (node.isElement())  // 如果节点是元素
                {
                    // 转换为元素
                    QDomElement e = node.toElement();
                    QString tagname = e.tagName();
                    qout << "tagname:" << tagname;
                    if (typeList.contains(tagname)) {
                        parseSVGTag(e, tagname);
                    }
                    else {
                        foreach(QString type, typeList) {
                            QDomNodeList list = e.elementsByTagName(type);
                            if (list.length() > 0) {
                                for (int i = 0; i < list.count(); i++) {
                                    QDomNode n = list.at(i);
                                    parseSVGTag(n.toElement(), n.nodeName());
                                }
                            }
                        }
                    }
                }
                // 下一个兄弟节点
                node = node.nextSibling();
            }
        }
    }
}

void SvgHelper::parseSVGTag(QDomElement e, QString tagname) {
    if (QString::compare(tagname, "path", Qt::CaseInsensitive) == 0) {  // 路径
        QString pathvalue = e.attribute("d");
        qout << pathvalue;
        parseSvgPath(pathvalue, paintPath);
    }
    else if (QString::compare(tagname, "rect", Qt::CaseInsensitive) == 0) {
        double x = getValueWithoutUnit(e.attribute("x"));
        double y = getValueWithoutUnit(e.attribute("y"));
        double width = getValueWithoutUnit(e.attribute("width"));
        double height = getValueWithoutUnit(e.attribute("height"));
        double rx = getValueWithoutUnit(e.attribute("rx"));
        double ry = getValueWithoutUnit(e.attribute("rx"));

        qout << x << y << width << height << rx << ry;

        if (rx < 0 || ry < 0) {
            paintPath.moveTo(x, y);
            paintPath.lineTo(x + width, y);
            paintPath.lineTo(x + width, y + height);
            paintPath.lineTo(x, y + height);
            paintPath.lineTo(x, y);
            paintPath.moveTo(x, y);
            {
                QList<QPointF> list;
                list.append(QPointF(x, y));
                list.append(QPointF(x + width, y));
                list.append(QPointF(x + width, y + height));
                list.append(QPointF(x, y));
                svgPointList.append(list);
            }
        }
        else {
            double r = qMax(rx, ry);
            paintPath.moveTo(x + r, y);
            paintPath.lineTo(x + width - r, y);
            QRectF rect(x + width - 2 * r, y, 2 * r, 2 * r);
            paintPath.arcTo(rect, 90, -90);
            paintPath.lineTo(x + width, y + height - r);
            rect = QRectF(x + width - 2 * r, y + height - 2 * r, 2 * r, 2 * r);
            paintPath.arcTo(rect, 0, -90);
            paintPath.lineTo(x + r, y + height);
            rect = QRectF(x, y + height - 2 * r, 2 * r, 2 * r);
            paintPath.arcTo(rect, -90, -90);
            paintPath.lineTo(x, y + r);
            rect = QRectF(x, y, 2 * r, 2 * r);
            paintPath.arcTo(rect, 180, -90);
            paintPath.moveTo(x + r, y);
            {
                QPainterPath path;
                QList<QPointF> list;
                list.append(QPointF(x + r, y));
                list.append(QPointF(x + width - r, y));

                QRectF rect(x + width - 2 * r, y, 2 * r, 2 * r);
                path.moveTo(x + width - r, y);
                path.arcTo(rect, 90, -90);
                for (double i = 0; i < 1; i += 1.0 / path.length())
                    list.append(path.pointAtPercent(i));
                list.append(path.pointAtPercent(1));

                list.append(QPointF(x + width, y + height - r));

                path.clear();
                rect = QRectF(x + width - 2 * r, y + height - 2 * r, 2 * r, 2 * r);
                path.arcTo(rect, 0, -90);
                for (double i = 0; i < 1; i += 1.0 / path.length())
                    list.append(path.pointAtPercent(i));
                list.append(path.pointAtPercent(1));

                list.append(QPointF(x + r, y + height));

                path.clear();
                rect = QRectF(x, y + height - 2 * r, 2 * r, 2 * r);
                path.arcTo(rect, -90, -90);
                for (double i = 0; i < 1; i += 1.0 / path.length())
                    list.append(path.pointAtPercent(i));
                list.append(path.pointAtPercent(1));

                list.append(QPointF(x, y + r));

                path.clear();
                rect = QRectF(x, y, 2 * r, 2 * r);
                path.arcTo(rect, 180, -90);
                for (double i = 0; i < 1; i += 1.0 / path.length())
                    list.append(path.pointAtPercent(i));
                list.append(path.pointAtPercent(1));

                list.append(QPointF(x + r, y));

                svgPointList.append(list);
            }
        }
    }
    else if (QString::compare(tagname, "circle", Qt::CaseInsensitive) == 0) {
        double cx = getValueWithoutUnit(e.attribute("cx"));
        double cy = getValueWithoutUnit(e.attribute("cy"));
        double r = getValueWithoutUnit(e.attribute("r"));

        cx = cx < 0 ? 0 : cx;
        cy = cy < 0 ? 0 : cy;

        qout << cx << cy << r;

        if (r > 0) {
            paintPath.moveTo(cx + r, cy);
            paintPath.arcTo(cx - r, cy - r, 2 * r, 2 * r, 0, 360);
            paintPath.moveTo(cx + r, cy);
            {
                QPainterPath path;
                QList<QPointF> list;

                path.moveTo(cx + r, cy);
                path.arcTo(cx - r, cy - r, 2 * r, 2 * r, 0, 360);
                for (double i = 0; i < 1; i += 1.0 / path.length())
                    list.append(path.pointAtPercent(i));
                list.append(path.pointAtPercent(1));
                svgPointList.append(list);
            }
        }
    }
    else if (QString::compare(tagname, "ellipse", Qt::CaseInsensitive) == 0) {
        double cx = getValueWithoutUnit(e.attribute("cx"));
        double cy = getValueWithoutUnit(e.attribute("cy"));
        double rx = getValueWithoutUnit(e.attribute("rx"));
        double ry = getValueWithoutUnit(e.attribute("ry"));

        cx = cx < 0 ? 0 : cx;
        cy = cy < 0 ? 0 : cy;

        qout << cx << cy << rx << ry;

        if (rx > 0 && ry > 0) {
            paintPath.moveTo(cx + rx, cy);
            paintPath.arcTo(cx - rx, cy - ry, 2 * rx, 2 * ry, 0, 360);
            paintPath.moveTo(cx + rx, cy);
            {
                QPainterPath path;
                QList<QPointF> list;

                path.moveTo(cx + rx, cy);
                path.arcTo(cx - rx, cy - ry, 2 * rx, 2 * ry, 0, 360);
                for (double i = 0; i < 1; i += 1.0 / path.length())
                    list.append(path.pointAtPercent(i));
                list.append(path.pointAtPercent(1));
                svgPointList.append(list);
            }
        }
    }
    else if (QString::compare(tagname, "line", Qt::CaseInsensitive) == 0) {
        double x1 = getValueWithoutUnit(e.attribute("x1"));
        double y1 = getValueWithoutUnit(e.attribute("y1"));
        double x2 = getValueWithoutUnit(e.attribute("x2"));
        double y2 = getValueWithoutUnit(e.attribute("y2"));

        qout << x1 << y1 << x2 << y2;

        paintPath.moveTo(x1, y1);
        paintPath.lineTo(x2, y2);
        paintPath.moveTo(x2, y2);
        {
            QList<QPointF> list;
            list.append(QPointF(x1, y1));
            list.append(QPointF(x2, y2));
            svgPointList.append(list);
        }
    }
    else if (QString::compare(tagname, "polygon", Qt::CaseInsensitive) == 0) {
        QString value = e.attribute("points");
        qout << value;
        QVector<double> vPos = segmentationCoordinates(value);
        qout << vPos;

        QPointF startPoint;
        QList<QPointF> list;
        if (vPos.length() > 1) {
            startPoint = QPointF(vPos[0], vPos[1]);
            paintPath.moveTo(startPoint);
            list.append(startPoint);
            vPos.removeFirst();
            vPos.removeFirst();
        }

        while (vPos.length() / 2 >= 1) {
            paintPath.lineTo(vPos[0], vPos[1]);
            list.append(QPointF(vPos[0], vPos[1]));
            vPos.removeFirst();
            vPos.removeFirst();
        }

        if (!startPoint.isNull()) {
            paintPath.lineTo(startPoint);
            paintPath.moveTo(startPoint);
            list.append(startPoint);
        }
        if (!list.isEmpty())
            svgPointList.append(list);

    }
    else if (QString::compare(tagname, "polyline", Qt::CaseInsensitive) == 0) {
        QString value = e.attribute("points");
        qout << value;
        QVector<double> vPos = segmentationCoordinates(value);
        qout << vPos;

        QPointF startPoint;
        QList<QPointF> list;
        if (vPos.length() > 1) {
            startPoint = QPointF(vPos[0], vPos[1]);
            paintPath.moveTo(startPoint);
            list.append(startPoint);
            vPos.removeFirst();
            vPos.removeFirst();
        }

        while (vPos.length() / 2 >= 1) {
            paintPath.lineTo(vPos[0], vPos[1]);
            list.append(QPointF(vPos[0], vPos[1]));
            vPos.removeFirst();
            vPos.removeFirst();
        }

        if (!startPoint.isNull()) {
            paintPath.moveTo(startPoint);
            //            list.append(startPoint);
        }
        if (!list.isEmpty())
            svgPointList.append(list);
    }

    if (!paintPath.isEmpty()) {
        svgPathList.append(paintPath);
    }
    paintPath.clear();
    if (!testpathlist.isEmpty()) {
        svgPointList.append(testpathlist);
    }
    testpathlist.clear();
}

void SvgHelper::parseSvgPath(QString path, QPainterPath& paintPath) {
    QString cmdLine = "";
    foreach(QChar c, path) {
        if (cmdList.contains(c)) {
            qout << cmdLine;
            if (!cmdLine.isEmpty()) {
                dealParsePainter(paintPath, cmdLine);
                cmdLine.clear();
            }
        }
        cmdLine += c;
    }
    if (!cmdLine.isEmpty()) {
        dealParsePainter(paintPath, cmdLine);
        cmdLine.clear();
    }
}

void SvgHelper::dealParsePainter(QPainterPath& path, QString line) {
    line = line.trimmed();
    int coordinates;
    QString cmd = line.mid(0, 1);
    QString value = line.mid(1);

    if (cmd.isUpper())
        coordinates = ABSOLUTE_COORDINATES;
    else
        coordinates = RELATIVE_COORDINATES;

    QVector<double> vNum = segmentationCoordinates(value);
    qout << vNum;

    switch (cmdTypeList.indexOf(cmd.toUpper().at(0))) {
    case 0:  // m
    {
        bool hasLineflag = vNum.length() > 2;
        bool lineto = false;

        if (!path.isEmpty())
            svgPathList.append(path);
        path.clear();
        if (!testpathlist.isEmpty())
            svgPointList.append(testpathlist);
        testpathlist.clear();

        while (vNum.length() / 2 >= 1) {
            QPointF point(vNum.at(0), vNum.at(1));
            if (coordinates == ABSOLUTE_COORDINATES) {
                nowPositon = point;
                if (lineto)
                    path.lineTo(nowPositon);
                else
                    path.moveTo(nowPositon);
                testpathlist.append(nowPositon);
            }
            else {
                nowPositon = nowPositon + point;
                if (lineto)
                    path.lineTo(nowPositon);
                else
                    path.moveTo(nowPositon);
                testpathlist.append(nowPositon);
            }
            qout << "nowPositon: " << nowPositon;
            if (!lineto)
                pathStartPosition = nowPositon;

            for (int i = 0; i < 2; i++)
                vNum.removeFirst();
            if (hasLineflag)
                lineto = true;
        }
        break;
    }
    case 1:  // l
    {
        while (vNum.length() / 2 >= 1) {
            QPointF point(vNum.at(0), vNum.at(1));
            if (coordinates == ABSOLUTE_COORDINATES) {
                path.lineTo(point);
                testpathlist.append(point);
                nowPositon = point;
            }
            else {
                QPointF targetPoint = nowPositon + point;
                path.lineTo(targetPoint);
                testpathlist.append(targetPoint);
                nowPositon = targetPoint;
            }
            qout << "nowPositon: " << nowPositon;
            path.moveTo(nowPositon);
            for (int i = 0; i < 2; i++)
                vNum.removeFirst();
        }
        break;
    }
    case 2:  // h
    {
        while (vNum.length() >= 1) {
            if (coordinates == ABSOLUTE_COORDINATES) {
                QPointF point(vNum.at(0), nowPositon.y());
                path.lineTo(point);
                testpathlist.append(point);
                nowPositon = point;
            }
            else {
                QPointF point(nowPositon.x() + vNum.at(0), nowPositon.y());
                path.lineTo(point);
                testpathlist.append(point);
                nowPositon = point;
            }
            qout << "nowPositon: " << nowPositon;
            path.moveTo(nowPositon);
            vNum.removeFirst();
        }
        break;
    }
    case 3:  // v
    {
        while (vNum.length() >= 1) {
            if (coordinates == ABSOLUTE_COORDINATES) {
                QPointF point(nowPositon.x(), vNum.at(0));
                path.lineTo(point);
                testpathlist.append(point);
                nowPositon = point;
            }
            else {
                QPointF point(nowPositon.x(), nowPositon.y() + vNum.at(0));
                path.lineTo(point);
                testpathlist.append(point);
                nowPositon = point;
            }
            qout << "nowPositon: " << nowPositon;
            path.moveTo(nowPositon);
            vNum.removeFirst();
        }
        break;
    }
    case 4:  // c
    {
        while (vNum.length() / 6 >= 1) {
            QPointF startPoint = nowPositon;
            QPointF c1, c2, endPoint;
            if (coordinates == ABSOLUTE_COORDINATES) {
                c1 = QPointF(vNum.at(0), vNum.at(1));
                c2 = QPointF(vNum.at(2), vNum.at(3));
                endPoint = QPointF(vNum.at(4), vNum.at(5));
            }
            else {
                double x = startPoint.x();
                double y = startPoint.y();
                c1 = QPointF(x + vNum.at(0), y + vNum.at(1));
                c2 = QPointF(x + vNum.at(2), y + vNum.at(3));
                endPoint = QPointF(x + vNum.at(4), y + vNum.at(5));
            }
            lastControlPosition = c2;
            path.moveTo(startPoint);
            path.cubicTo(c1, c2, endPoint);  // 三次贝塞尔曲线
            path.moveTo(endPoint);

            {
                QPainterPath testpath;

                testpath.moveTo(startPoint);
                testpath.cubicTo(c1, c2, endPoint);  // 三次贝塞尔曲线
                testpath.moveTo(endPoint);
                for (double i = 0; i <= 1; i += 1.0 / testpath.length())
                    testpathlist.append(testpath.pointAtPercent(i));
            }

            nowPositon = endPoint;
            qout << "nowPositon: " << nowPositon;

            for (int i = 0; i < 6; i++)
                vNum.removeFirst();
        }
        break;
    }
    case 5:  // s
    {
        while (vNum.length() / 4 >= 1) {
            QPointF startPoint = nowPositon;
            QPointF c1, c2, endPoint;
            if (coordinates == ABSOLUTE_COORDINATES) {
                double x = startPoint.x();
                double y = startPoint.y();
                c1 = QPointF(2 * x - lastControlPosition.x(),
                    2 * y - lastControlPosition.y());
                c2 = QPointF(vNum.at(0), vNum.at(1));
                endPoint = QPointF(vNum.at(2), vNum.at(3));
            }
            else {
                double x = startPoint.x();
                double y = startPoint.y();
                c1 = QPointF(x + x - lastControlPosition.x(),
                    y + y - lastControlPosition.y());
                c2 = QPointF(x + vNum.at(0), y + vNum.at(1));
                endPoint = QPointF(x + vNum.at(2), y + vNum.at(3));
            }
            lastControlPosition = c2;
            path.moveTo(startPoint);
            path.cubicTo(c1, c2, endPoint);  // 三次贝塞尔曲线
            path.moveTo(endPoint);

            {
                QPainterPath testpath;

                testpath.moveTo(startPoint);
                testpath.cubicTo(c1, c2, endPoint);  // 三次贝塞尔曲线
                testpath.moveTo(endPoint);
                for (double i = 0; i <= 1; i += 1.0 / testpath.length())
                    testpathlist.append(testpath.pointAtPercent(i));
            }

            nowPositon = endPoint;
            qout << "nowPositon: " << nowPositon;

            for (int i = 0; i < 4; i++)
                vNum.removeFirst();
        }

        break;
    }
    case 6:  // q
    {
        while (vNum.length() / 4 >= 1) {
            QPointF startPoint = nowPositon;
            QPointF cPoint;
            QPointF endPoint;
            if (coordinates == ABSOLUTE_COORDINATES) {
                cPoint = QPointF(vNum.at(0), vNum.at(1));
                endPoint = QPointF(vNum.at(2), vNum.at(3));
            }
            else {
                double x = startPoint.x();
                double y = startPoint.y();
                cPoint = QPointF(x + vNum.at(0), y + vNum.at(1));
                endPoint = QPointF(x + vNum.at(2), y + vNum.at(3));
            }
            lastControlPosition = cPoint;
            path.moveTo(startPoint);
            path.quadTo(cPoint, endPoint);  // 二次贝塞尔曲线
            path.moveTo(endPoint);

            {
                QPainterPath testpath;

                testpath.moveTo(startPoint);
                testpath.quadTo(cPoint, endPoint);  // 二次贝塞尔曲线
                testpath.moveTo(endPoint);
                for (double i = 0; i <= 1; i += 1.0 / testpath.length())
                    testpathlist.append(testpath.pointAtPercent(i));
            }

            nowPositon = endPoint;
            qout << "nowPositon: " << nowPositon;

            for (int i = 0; i < 4; i++)
                vNum.removeFirst();
        }
        break;
    }
    case 7:  // t
    {
        while (vNum.length() / 2 >= 1) {
            QPointF startPoint = nowPositon;
            QPointF cPoint;
            QPointF endPoint;
            if (coordinates == ABSOLUTE_COORDINATES) {
                double x = startPoint.x();
                double y = startPoint.y();
                cPoint = QPointF(2 * x - lastControlPosition.x(),
                    2 * y - lastControlPosition.y());
                endPoint = QPointF(vNum.at(0), vNum.at(1));
            }
            else {
                double x = startPoint.x();
                double y = startPoint.y();
                cPoint = QPointF(x + x - lastControlPosition.x(),
                    y + y - lastControlPosition.y());
                endPoint = QPointF(x + vNum.at(0), y + vNum.at(1));
            }
            lastControlPosition = cPoint;
            path.moveTo(startPoint);
            path.quadTo(cPoint, endPoint);  // 二次贝塞尔曲线
            path.moveTo(endPoint);

            {
                QPainterPath testpath;

                testpath.moveTo(startPoint);
                testpath.quadTo(cPoint, endPoint);  // 二次贝塞尔曲线
                testpath.moveTo(endPoint);
                for (double i = 0; i <= 1; i += 1.0 / testpath.length())
                    testpathlist.append(testpath.pointAtPercent(i));
            }

            nowPositon = endPoint;
            qout << "nowPositon: " << nowPositon;

            for (int i = 0; i < 2; i++)
                vNum.removeFirst();
        }
        break;
    }
    case 8:  // a
    {
        while (vNum.length() / 7 >= 1) {
            QPointF startPoint = nowPositon;
            double rx = vNum.at(0);
            double ry = vNum.at(1);
            double x_axis_rotation = vNum.at(2);
            double large_arc_flag = vNum.at(3);
            double sweep_flag = vNum.at(4);
            QPointF endPoint;

            double cx;
            double cy;
            double start_angle;
            double delta_angle;

            if (coordinates == ABSOLUTE_COORDINATES) {
                endPoint = QPointF(vNum.at(5), vNum.at(6));
            }
            else {
                double x = startPoint.x();
                double y = startPoint.y();
                endPoint = QPointF(x + vNum.at(5), y + vNum.at(6));
            }

            // svg : [A | a] (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
            // x1 y1 rx ry φ fA fS x2 y2
            svgArcToCenterParam(startPoint.x(), startPoint.y(), rx, ry,
                x_axis_rotation, large_arc_flag, sweep_flag,
                endPoint.x(), endPoint.y(), cx, cy, start_angle,
                delta_angle);

            start_angle = qRadiansToDegrees(start_angle);
            delta_angle = qRadiansToDegrees(delta_angle);

            QRectF rect(cx - rx, cy - ry, 2 * rx, 2 * ry);

            qout << "converted: " << cx << cy << start_angle << delta_angle << rect;

            if (delta_angle != 0) {
                path.moveTo(startPoint);
                path.arcTo(rect, 360 - start_angle,
                    -delta_angle);  // 绘图的坐标系与笛卡尔坐标系不同, 需要转换

                {
                    QPainterPath testpath;

                    testpath.moveTo(startPoint);
                    testpath.arcTo(rect, 360 - start_angle, -delta_angle);
                    testpath.moveTo(endPoint);
                    for (double i = 0; i <= 1; i += 1.0 / testpath.length())
                        testpathlist.append(testpath.pointAtPercent(i));
                }
            }
            path.moveTo(endPoint);

            nowPositon = endPoint;
            qout << "nowPositon: " << nowPositon;

            for (int i = 0; i < 7; i++)
                vNum.removeFirst();
        }

        break;
    }
    case 9:  // z
        qout << "z:" << pathStartPosition;
        path.lineTo(pathStartPosition);
        testpathlist.append(pathStartPosition);
        break;
    }
}

QVector<double> SvgHelper::segmentationCoordinates(QString value) {
    // 将科学记数法暂时替换, 防止分割出错
    if (value.contains("e", Qt::CaseInsensitive)) {
        value.replace("e-", "[KXJSFF]");
        value.replace("E-", "[KXJSFF]");
    }

    if (value.contains(" -")) {
        value.replace(" -", " [KGFS]");
    }

    if (value.contains(",-")) {
        value.replace(",-", ",[DHFS]");
    }

    qout << "------value:------" << value;
    QVector<double> vPos;
    QString num = "";
    foreach(QChar c, value) {
        if (splitList.contains(c)) {
            num = num.trimmed();
            if (!num.isEmpty()) {
                if (num.contains(','))
                    num.remove(',');
                vPos.append(getValueWithoutUnit(num));
                num.clear();
            }
        }
        num += c;
    }
    num = num.trimmed();
    if (num.contains(','))
        num.remove(',');
    if (!num.isEmpty())
        vPos.append(getValueWithoutUnit(num));
    qout << "------num:------" << num;
    return vPos;
}

double SvgHelper::getValueWithoutUnit(QString input) {
    // 将科学记数法替换回来
    if (input.contains("[KXJSFF]"))
        input.replace("[KXJSFF]", "e-");
    if (input.contains("[KGFS]"))
        input.replace("[KGFS]", "-");
    if (input.contains("[DHFS]"))
        input.replace("[DHFS]", "-");
    qout << "------input:------" << input;

    if (input.isEmpty())
        return 0;
    QString str = input;
    foreach(QString unit, unitList) {
        if (str.contains(unit)) {
            str.remove(unit);
        }
    }
    return str.toDouble();
}

/**
svg : [A | a] (rx ry x-axis-rotation large-arc-flag sweep-flag x2 y2)+

(x1 y1)圆弧路径起点
(x2 y2)圆弧路径终点

rx 椭圆弧的X半轴长度
ry 椭圆弧的Y半轴长度
x-axis-rotation 椭圆弧X轴方向的旋转角度
large-arc-flag 标记是否大弧段
sweep-flag 标记是否顺时针绘制

sample :  svgArcToCenterParam(200,200,50,50,0,1,1,300,200, output...)
*/
double SvgHelper::radian(double ux, double uy, double vx, double vy) {
    double dot = ux * vx + uy * vy;
    double mod = sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
    double rad = acos(dot / mod);
    if (ux * vy - uy * vx < 0.0)
        rad = -rad;
    return rad;
}

int SvgHelper::svgArcToCenterParam(double x1,
    double y1,
    double rx,
    double ry,
    double phi,
    double fA,
    double fS,
    double x2,
    double y2,
    double& cx_out,
    double& cy_out,
    double& startAngle_out,
    double& deltaAngle_out) {
    double cx, cy, startAngle, deltaAngle, endAngle;
    double PIx2 = M_PI * 2.0;

    if (rx < 0) {
        rx = -rx;
    }
    if (ry < 0) {
        ry = -ry;
    }
    if (rx == 0.0 || ry == 0.0) {  // invalid arguments
        return -1;
    }

    double s_phi = sin(phi);
    double c_phi = cos(phi);
    double hd_x = (x1 - x2) / 2.0;  // half diff of x
    double hd_y = (y1 - y2) / 2.0;  // half diff of y
    double hs_x = (x1 + x2) / 2.0;  // half sum of x
    double hs_y = (y1 + y2) / 2.0;  // half sum of y

    // F6.5.1
    double x1_ = c_phi * hd_x + s_phi * hd_y;
    double y1_ = c_phi * hd_y - s_phi * hd_x;

    // F.6.6 Correction of out-of-range radii
    //   Step 3: Ensure radii are large enough
    double lambda = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
    if (lambda > 1) {
        rx = rx * sqrt(lambda);
        ry = ry * sqrt(lambda);
    }

    double rxry = rx * ry;
    double rxy1_ = rx * y1_;
    double ryx1_ = ry * x1_;
    double sum_of_sq = rxy1_ * rxy1_ + ryx1_ * ryx1_;  // sum of square
    if (!sum_of_sq) {
        return -1;
    }
    double coe = sqrt(abs((rxry * rxry - sum_of_sq) / sum_of_sq));
    if (fA == fS) {
        coe = -coe;
    }

    // F6.5.2
    double cx_ = coe * rxy1_ / ry;
    double cy_ = -coe * ryx1_ / rx;

    // F6.5.3
    cx = c_phi * cx_ - s_phi * cy_ + hs_x;
    cy = s_phi * cx_ + c_phi * cy_ + hs_y;

    double xcr1 = (x1_ - cx_) / rx;
    double xcr2 = (x1_ + cx_) / rx;
    double ycr1 = (y1_ - cy_) / ry;
    double ycr2 = (y1_ + cy_) / ry;

    // F6.5.5
    startAngle = radian(1.0, 0.0, xcr1, ycr1);

    // F6.5.6
    deltaAngle = radian(xcr1, ycr1, -xcr2, -ycr2);
    while (deltaAngle > PIx2) {
        deltaAngle -= PIx2;
    }
    while (deltaAngle < 0.0) {
        deltaAngle += PIx2;
    }
    if (fS == 0) {
        deltaAngle -= PIx2;
    }
    endAngle = startAngle + deltaAngle;
    while (endAngle > PIx2) {
        endAngle -= PIx2;
    }
    while (endAngle < 0.0) {
        endAngle += PIx2;
    }

    cx_out = cx;
    cy_out = cy;
    startAngle_out = startAngle;
    deltaAngle_out = deltaAngle;

    return 1;
}

QList<QList<QPointF> > SvgHelper::getSvgPointList() const {
    return svgPointList;
}

QList<QPainterPath> SvgHelper::getSvgPathList() const {
    return svgPathList;
}

/*
    get angle ACB, point C is the center point
    A(x1,y1)
    B(x2,y2)
    C(x3,y3)
*/
double SvgHelper::get_angle_with_points(double x1,
    double y1,
    double x2,
    double y2,
    double x3,
    double y3) {
    double theta = atan2(x1 - x3, y1 - y3) - atan2(x2 - x3, y2 - y3);
    if (theta > M_PI)
        theta -= 2 * M_PI;
    if (theta < -M_PI)
        theta += 2 * M_PI;

    theta = abs(theta * 180.0 / M_PI);

    if (y2 <= y3) {
        theta = 360.0 - theta;
    }

    return theta;
}